package com.blacktry.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.blacktry.entity.easypoidata.DeriveExcel;
import com.blacktry.entity.model.Admin;
import com.blacktry.entity.model.ExamManage;
import com.blacktry.service.AdminService;
import com.blacktry.service.AliOssService;
import com.blacktry.entity.model.OssEntity;
import com.blacktry.service.DeriveExcelService;
import com.blacktry.service.ExamManageService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 在创建这个AliOssServiceImpl的时候就应该把属性值装载好
 */
@Service
public class AliOssServiceImpl implements AliOssService, InitializingBean {

    @Autowired
    private OssEntity ossEntity;

    @Autowired
    private DeriveExcelService deriveExcelService;

    /**
     * Endpoint以杭州为例，其它Region请按实际情况填写。
     */
    private String endpoint;
    /**
     * 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录RAM控制台创建RAM账号。
     */
    private String accessKeyId;
    private String accessKeySecret;
    /**
     * <yourObjectName>从OSS下载文件时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
     */
    private String bucketName;


    /**
     * 初始化bean之后需要进行的操作
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // Endpoint以杭州为例，其它Region请按实际情况填写。
        endpoint = ossEntity.getEndpoint();
        // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录RAM控制台创建RAM账号。
        accessKeyId = ossEntity.getAccessKeyId();
        accessKeySecret = ossEntity.getAccessKeySecret();
        bucketName = ossEntity.getBucketName();
    }

    /**
     * 创建存储空间
     */
    @Override
    public void createBucket() {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        //判断bucketName是否存在
        if (ossClient.doesBucketExist(bucketName)) {
            throw new RuntimeException(bucketName + "在对象存储的Bucket列表中已经存在");
        }
        // 创建存储空间。
        ossClient.createBucket(bucketName);
        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * 上传文件
     *
     * @param file 文件对象
     * @return
     */
    @Override
    public String upload(MultipartFile file) {
        //上传的地址
        String uploadUrl = null;
        try {
            // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            //判断bucketName是否存在
            if (!ossClient.doesBucketExist(bucketName)) {
                //创建bucket
                ossClient.createBucket(bucketName);
                //设置bucket的属性
                ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
            }
            //获取上传的文件流
            InputStream inputStream = file.getInputStream();

            //构建日期的文件夹路径  avatar/年/月/日/文件名
            String datePath = new DateTime().toString("yyyy/MM/dd");
//            String datePath =  new SimpleDateFormat("yyyy/MM/dd").format(new Date());

            //获取上传文件的全名称
            String original = file.getOriginalFilename();

            //获取UUID并去除-
            String fileName = UUID.randomUUID().toString().replaceAll("-", "");

            //获取上传文件的扩展名
            String fileType = original.substring(original.lastIndexOf("."));

            //拼接文件名称
            String newName = fileName + fileType;

            //生成文件夹
            fileName = datePath + "/" + newName;

            //如果想要实现图片预览的效果,一定要设置以下几点
            //1.设置文件的ACL(权限)  要么是公共读,要么是公共读写
            //2.一定要设置文本类型(image/jpg)
            ObjectMetadata objectMetadata = new ObjectMetadata();
            //设置公共读权限
            objectMetadata.setObjectAcl(CannedAccessControlList.PublicRead);
            objectMetadata.setContentType(getcontentType(fileType));

            //每次上传得到的名字肯定是不能相同的,在java中如何让每次生成的名字不一样呢?
            //uuid  redis分布式ID 雪花算法   为了更加方便的区分,这边的文件格式yyyy/MM/dd+uuid
            ossClient.putObject(bucketName, fileName, inputStream, objectMetadata);

            // 关闭OSSClient。
            ossClient.shutdown();

            //默认十年不过期
            Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 10);

            //bucket名称  文件名   过期时间
            uploadUrl = ossClient.generatePresignedUrl(bucketName, fileName, expiration).toString();

            //获取url地址
            //uploadUrl = "https://" + bucketName + "." + endPoint + "/" + fileName;

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 去除参数
        return uploadUrl.substring(0, uploadUrl.indexOf("?"));
        //return uploadUrl;
    }

    public String uploadPathID(MultipartFile file,String examId) {
        //上传的地址
        String uploadUrl = null;
        try {
            // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            //判断bucketName是否存在
            if (!ossClient.doesBucketExist(bucketName)) {
                //创建bucket
                ossClient.createBucket(bucketName);
                //设置bucket的属性
                ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
            }
            //获取上传的文件流
            InputStream inputStream = file.getInputStream();
            //构建日期的文件夹路径  avatar/年/月/日/文件名
            String datePath = new DateTime().toString(examId+"/yyyy/MM/dd");
//            String datePath =  new SimpleDateFormat("yyyy/MM/dd").format(new Date());
            //获取上传文件的全名称
            String original = file.getOriginalFilename();
            //获取UUID并去除-
            String fileName = UUID.randomUUID().toString().replaceAll("-", "");
            //获取上传文件的扩展名
            String fileType = original.substring(original.lastIndexOf("."));
            //拼接文件名称
            String newName = fileName + fileType;
            //生成文件夹
            fileName = datePath + "/" + newName;
            //如果想要实现图片预览的效果,一定要设置以下几点
            //1.设置文件的ACL(权限)  要么是公共读,要么是公共读写
            //2.一定要设置文本类型(image/jpg)
            ObjectMetadata objectMetadata = new ObjectMetadata();
            //设置公共读权限
            objectMetadata.setObjectAcl(CannedAccessControlList.PublicRead);
            objectMetadata.setContentType(getcontentType(fileType));
            //每次上传得到的名字肯定是不能相同的,在java中如何让每次生成的名字不一样呢?
            //uuid  redis分布式ID 雪花算法   为了更加方便的区分,这边的文件格式yyyy/MM/dd+uuid
            ossClient.putObject(bucketName, fileName, inputStream, objectMetadata);
            // 关闭OSSClient。
            ossClient.shutdown();
            //默认十年不过期
            Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 10);
            //bucket名称  文件名   过期时间
            uploadUrl = ossClient.generatePresignedUrl(bucketName, fileName, expiration).toString();
            //获取url地址
            //uploadUrl = "https://" + bucketName + "." + endPoint + "/" + fileName;
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 去除参数
        return uploadUrl.substring(0, uploadUrl.indexOf("?"));
        //return uploadUrl;
    }

    @Autowired
    ExamManageService examManageService;
    @Autowired
    private AdminService adminService;

    @Override
    public List<String> uploadFiles(MultipartFile[] files,String examId,String username){
        List<String> filesUrl = new ArrayList<>();
        List<DeriveExcel> deriveExcels = new ArrayList<>();
        String examDate = examManageService.selectExamOne(Integer.parseInt(examId)).getExamDate();
        for (int i = 0; i < files.length; i++) {
            String url = uploadPathID(files[i],examId);
            DeriveExcel deriveExcel = new DeriveExcel();
            deriveExcel.setExamId(examId);
            try {
                deriveExcel.setPaperImg(new URL(url));
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            deriveExcel.setPaper(url);
            //交卷时间
            Date date = new Date();
            SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
            deriveExcel.setAnswerDate(dateFormat.format(new java.util.Date()));
            //答题用时
            long minute= DateUtil.between(DateUtil.parse(examDate,"yyyy-MM-dd HH:mm:ss"),DateUtil.parse(deriveExcel.getAnswerDate(),"yyyy-MM-dd HH:mm:ss:SSS"), DateUnit.MS);
            deriveExcel.setTime(String.valueOf(minute));
            // 提交人username
            deriveExcel.setCommitUsername(username);
            // 提交人的身份
            Admin admin = adminService.selectByUsername(username);
            deriveExcel.setCommitRole(admin.getCompetent());
            if("学生".equals(deriveExcel.getCommitRole())){
                // 如果提交人是学生本人,将username设为自己
                deriveExcel.setUsername(deriveExcel.getCommitUsername());
                deriveExcel.setName(admin.getName());
                deriveExcel.setAffiliation(admin.getAffiliation());

                //考卷状态处理
                //查询出学生之前的提交记录
//                String userName=deriveExcel.getUsername();
                List<DeriveExcel> list=deriveExcelService.selectDeriveByExamIdAndUsername(examId,username);
                for(DeriveExcel deriveExcel1:list){
                    deriveExcel1.setEnable(false);
                    deriveExcelService.updateOrInsert(deriveExcel1);
//                    Integer id=deriveExcel1.getId();
//                    Boolean stat=deriveExcel1.getEnable();
//                    examManageService.updateExamState(id,examId,username,stat);
                }

            }else{
                // 如果是老师，将username设为‘待改卷无法确定身份’,身份待识别时候填入
                deriveExcel.setUsername("待改卷无法确定身份");
            }

            deriveExcels.add(deriveExcel);
            filesUrl.add(url);
        }
        // 将examId绑定的url上传到数据库
        deriveExcelService.saveBatch(deriveExcels);
        return filesUrl;
    }

    /**
     * 下载文件
     *
     * @param fileName
     * @throws IOException
     */
    @Override
    public void download(String fileName) throws IOException {
        // <yourObjectName>从OSS下载文件时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
        String objectName = fileName;

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 调用ossClient.getObject返回一个OSSObject实例，该实例包含文件内容及文件元信息。
        OSSObject ossObject = ossClient.getObject(bucketName, objectName);
        // 调用ossObject.getObjectContent获取文件输入流，可读取此输入流获取其内容。
        InputStream content = ossObject.getObjectContent();
        if (content != null) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(content));
            while (true) {
                String line = reader.readLine();
                if (line == null) {
                    break;
                }
                System.out.println("\n" + line);
            }
            // 数据读取完成后，获取的流必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
            content.close();
        }
        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * 列举文件
     */
    @Override
    public void listFile() {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // ossClient.listObjects返回ObjectListing实例，包含此次listObject请求的返回结果。
        ObjectListing objectListing = ossClient.listObjects(bucketName);
        // objectListing.getObjectSummaries获取所有文件的描述信息。
        for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
            System.out.println(" - " + objectSummary.getKey() + "  " +
                    "(size = " + objectSummary.getSize() + ")");
        }

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * 删除文件
     *
     * @param fileName
     */
    @Override
    public void deleteFile(String fileName) {
        // <yourObjectName>从OSS下载文件时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
        String objectName = fileName;

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 删除文件。
        ossClient.deleteObject(bucketName, objectName);

        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * Description: 判断OSS服务文件上传时文件的contentType
     *
     * @param FilenameExtension 文件后缀
     * @return String
     */
    public static String getcontentType(String FilenameExtension) {
        if (FilenameExtension.equalsIgnoreCase(".bmp")) {
            return "image/bmp";
        }
        if (FilenameExtension.equalsIgnoreCase(".gif")) {
            return "image/gif";
        }
        if (FilenameExtension.equalsIgnoreCase(".jpeg") ||
                FilenameExtension.equalsIgnoreCase(".jpg") ||
                FilenameExtension.equalsIgnoreCase(".png")) {
            return "image/jpg";
        }
        if (FilenameExtension.equalsIgnoreCase(".html")) {
            return "text/html";
        }
        if (FilenameExtension.equalsIgnoreCase(".txt")) {
            return "text/plain";
        }
        if (FilenameExtension.equalsIgnoreCase(".vsd")) {
            return "application/vnd.visio";
        }
        if (FilenameExtension.equalsIgnoreCase(".pptx") ||
                FilenameExtension.equalsIgnoreCase(".ppt")) {
            return "application/vnd.ms-powerpoint";
        }
        if (FilenameExtension.equalsIgnoreCase(".docx") ||
                FilenameExtension.equalsIgnoreCase(".doc")) {
            return "application/msword";
        }
        if (FilenameExtension.equalsIgnoreCase(".xml")) {
            return "text/xml";
        }
        return "image/jpg";
    }

}